import Token
import Editor
import string
import CmdNo
import RegisterInit,RegisterImpl
#############################################################################
gDumpToken=0 # set to 1 to print the entered tokens

def SetKeyHandlerFactory(classobject):
	global _gHandlerFactory
	_gHandlerFactory=classobject
def MakeKeyHandler(editor):
	return _gHandlerFactory(editor)

eShiftPrefix	= 1
eControlPrefix	= 2
eMetaPrefix	 = 4

_AnyOtherKey="AnyOtherKey" 
# class KeyHandler
class KeyHandler:
	# the famos init...
	def __init__(self,editor):
		self.keymaps={}
		self.editor=editor
		self.keymap=self.NewKeyMap()
		self.currkeymap=self.keymap
		self.pendingKeySeq=None
		self.currKeySeq=None
		self.currToken=None
		self.prefix=0

		self.keyhooks={}
		self.prefixMap={}
		self.InitPrefixes()

		self.shortcuts={}
		self.shortcutkeys=[]
		self.shortcutEditors={}
		## last statement!
		self.InitKeymap()
		self.selstart=None
		
		self.macro=[]
		self.defmacro=None

	def NewKeyMap(self):
		keymap={}
		self.InitNewKeyMap(keymap)
		return keymap

	def InitNewKeyMap(self,keymap):
		self.KeymapAddKey(keymap,_AnyOtherKey,self.ForwardAscii)
		#self.KeymapAddKey(keymap,_AnyOtherKey,self.Cancel)

	def InitPrefixes(self):
##		self.AddMetaPrefix("Esc")
		self.AddMetaPrefix("C-[")
##		self.AddMetaPrefix("F2")
##		self.AddControlPrefix("F3")
##		self.AddShiftPrefix("F4")

	def KeymapRemoveKey(self,keymap,key):
		try:
			del keymap[key]
		except KeyError:
			pass

	def KeymapAddKey(self,keymap,key,action,args=()):
		Token.ValidateKeyName(key) # raises an KeyError if key....
		if type(args) != type(()):
			args=(args,)
		keys=string.split(key)
		# if we have a multiple key....
		for key in keys[:-1]:
			if not keymap.has_key(key):
				keymap[key]=self.NewKeyMap()
			keymap=keymap[key]
		keymap[keys[-1]]=(action,args)
		
	def BindKey(self,key,action,*args):
		self.KeymapAddKey(self.keymap,key,action,args)

	def SetShortcut(self,key,cmd):
		self.BindKey(key,Editor.DoMenuCommand,cmd)
		#self.editor.SetMenuShortcut(cmd,key)
		self.shortcuts[key]=cmd
		self.shortcutEditors={}
		self.shortcutkeys.append(key)

	def UpdateShortcuts(self):
		if not self.shortcutEditors.has_key(self.editor):
			import regex,string
			editor=self.editor
			for key in self.shortcutkeys:
				cmd=self.shortcuts[key]
				if regex.match("M-.",key)>0:
					# fake the SNiFF shortcuts!
					key=string.upper(key[2])
				try:
					editor.SetMenuShortcut(cmd,key)
				except:
					pass # ignore errors....
			self.shortcutEditors[editor]=1

	def DoSetupMenu(self):
		return
		self.UpdateShortcuts()

	def SetCurrKeymap(self,keymap):
		self.currkeymap=keymap

	def ResetKeymap(self):
		self.currkeymap=self.keymap

	def DoKey(self):
		Editor.DoToken(self.currToken)

	def ForwardAscii(self):
		"""Insert the current character into the Text.
		Ignore any prefixes."""
		token=self.currToken
##		if token.IsPrintable() and not token.IsMeta() and not token.IsControl():
		if token.IsAscii() and not token.IsMeta() and not token.IsControl():
			Editor.DoToken(token)
		elif token.IsCursorKey() or token.IsOtherFunctionKey():
			Editor.DoToken(token)
		else:
			Editor.Bell()

	def HandleKey(self,code,flags):
		#Editor.SetEditor(self.editor)
		#self.UpdateShortcuts()
		token=Token.Token(code,flags)
		if gDumpToken:
			token.Dump()
		key=token.KeyName()
		if self.HandlePrefix(key):
			return
		if self.prefix:
			self.SetTokenPrefix(token,self.prefix)
			self.prefix=0
			key=token.KeyName()
		Editor.gCurrentToken=token
		self.currToken=token

		keymap=self.currkeymap
		if keymap.has_key(key):
			value=keymap[key]
		elif keymap.has_key(_AnyOtherKey):
			value=keymap[_AnyOtherKey]
		else:
			return
		if type(value) == type(keymap):
			self.DoMultipleKey(key,keymap[key])
			return
		if self.pendingKeySeq:
			currkey=self.pendingKeySeq + ' ' + key
			self.EndMultipleKey()
		else:
			currkey=key
		(action,args)=value
		self.PerformKey(currkey,token,action,args)
		self.UpdateSel()
		
	def PerformKey(self,key,token,action,args):
		self.currToken=token
		self.currKeySeq=key
		if self.keyhooks:
			self.DoKeyHooks(key,token,action,args)
		apply(action,args)
	
	def DoMultipleKey(self,key,keymap):
		self.currkeymap=keymap
		if self.pendingKeySeq:
			self.pendingKeySeq=self.pendingKeySeq + " " + key
		else:
			self.pendingKeySeq=key

	def EndMultipleKey(self):
		self.currkeymap=self.keymap
		self.pendingKeySeq=None
		self.prefix=0

	def Cancel(self):
		"""Cancel the current key sequence."""
		Editor.Bell()
		self.Reset()
 
	def Reset(self):
		try:
			viewtype=self.editor.GetViewType()
		except:
			viewtype="TextView"
		try:
			self.keymap=self.keymaps[viewtype]
		except KeyError:
			self.keymap=self.NewKeyMap()
			self.InitKeymap(viewtype)
		self.EndMultipleKey()
		self.ResetSelStart()
		self.CancelMacro()

	def SetTokenPrefix(self,token,prefix):
		if prefix & eMetaPrefix:
			token.SetMeta()
		if prefix & eShiftPrefix:
			token.SetShift()
		if prefix & eControlPrefix:
			token.SetControl()


	def AddMetaPrefix(self,keyname):
		"""Set an altetnative key for M-"""
		self.prefixMap[keyname]=eMetaPrefix
	def AddControlPrefix(self,keyname):
		"""Set an altetnative key for C-"""
		self.prefixMap[keyname]=eControlPrefix
	def AddShiftPrefix(self,keyname):
		"""Set an altetnative key for S-"""
		self.prefixMap[keyname]=eShiftPrefix
	def HandlePrefix(self,keyname):
		if not self.prefixMap.has_key(keyname):
			return 0
		if self.prefix & self.prefixMap[keyname]:
			return 0
		self.prefix=self.prefix|self.prefixMap[keyname]
		return 1

	def DoKeyHooks(self,key,token,action,args):
		for k in self.keyhooks.keys():
			self.keyhooks[k](key,token,action,args)

	def AddKeyHook(self,name,func):
		self.keyhooks[name]=func
	def DelKeyHook(self,name):
		del self.keyhooks[name]
	def ToggleKeyHook(self,name,func):
		if self.keyhooks.has_key(name):
			del self.keyhooks[name]
		else:
			self.keyhooks[name]=func

	def ToggleHelp(self):
		self.ToggleKeyHook('HELP',self.PrintHelp)

	def PrintHelp(self,key,token,action,args):
		print `key`
		token.Dump()
		#FunctionHelp.Print(action)

	def StartMacro(self):
		"""Start recording a keyboard macro."""
		self.AddKeyHook('MACRO',self.AddToMacro)
		self.defmacro=[]
	def EndMacro(self):
		"""End the recording a keyboard macro."""
		self.macro=self.defmacro[:-1] # don't run this...
		self.defmacro=None
		self.DelKeyHook('MACRO')
	def CancelMacro(self):
		"""Cancel the recording of the current keyboard macro"""
 
		if self.defmacro is not None:
			self.defmacro=None
			self.DelKeyHook('MACRO')
	def RunMacro(self):
		"""Run the current keyboard macro"""
		for key,token,action,args in self.macro:
			self.PerformKey(key,token,action,args)
		#self.PrintMacro()
	def AddToMacro(self,key,token,action,args):
		self.defmacro.append((key,token,action,args))
	def PrintMacro(self):
		for key,token,action,args in self.macro:
			print key,token,action,args

	def Zap(self):
		"""Kill up to and including first occurrence of CHAR"""
		token=self.currToken
		if token.IsPrintable() and not token.IsMeta() and not token.IsControl():
			Editor.Zap(token.ASCII())
		else:
			Editor.Bell()

	def SetSelStart(self):
		if self.selstart:
			self.selstart=None
		else:
			self.selstart=Editor.GetSelection()
	def ResetSelStart(self):
		self.selstart=None
	def UpdateSel(self):
		if not self.selstart:
			return
		oldstart,oldend=self.selstart
		newstart,newend=Editor.GetSelection()
		if newend < oldstart:
			newend=oldstart
		elif newstart > oldend:
			newstart=oldend

		Editor.SetSelection(min(newstart,oldstart),max(newend,oldend))
	def InitKeymap(self,viewtype='TextView'):
		#print 'InitKeymap',viewtype
		RegisterImpl._keys.CallAll("",(self,))
		RegisterImpl._keys.CallAll(viewtype,(self,))
		#print '...InitKeymap'

_gHandlerFactory=KeyHandler
